import { RouteObject } from 'react-router-dom';
import {
  ComponentType,
  createElement,
  PropsWithChildren,
  useContext,
  useLayoutEffect,
} from 'react';

import { LazyRouteFunction } from 'react-router';
import { useCurrentRoute } from 'src/hooks';
import { ConfigContext } from 'antd/es/config-provider';

export interface PageMetaInfo {
  title?: Record<string, string>;
  permissions?: string[];
}

export interface PageMeta {
  meta?: PageMetaInfo;
  [key: string]: unknown;
}

export type RouterConfigObject = PageMeta & RouteObject;

export type LazyElementCallback = () => Promise<{
  default: ComponentType;
}>;

export function defineRouteConfig(
  config: RouterConfigObject,
  modules?: object,
) {
  if (config.element) {
    const Element = (props: PropsWithChildren) => {
      const ac = useContext(ConfigContext);
      useLayoutEffect(() => {
        const lang = ac?.locale?.locale;
        if (config?.meta?.title && lang && config.meta.title[lang]) {
          document.title = config.meta.title[lang];
        }
      }, [ac?.locale?.locale]);
      return props.children;
    };
    config.element = createElement(Element, {
      children: config.element,
    });
  }

  if (modules && Object.keys(modules).length) {
    config.children = (config.children || []).concat(
      ...Object.values(modules).map((o) => o.default),
    );
    if (config.children.length) {
      const find = config.children.find((o) => o.index);
      if (!find) {
        const { path, ...rest } = config.children[0];
        config.children.push({
          alias: path,
          path: '',
          ...rest,
        } as unknown as RouteObject);
      }
    }
  }

  config?.children?.map((o) => {
    (o as { parent: RouterConfigObject }).parent = config;
  });
  return config;
}

export function lazy(
  load: LazyElementCallback,
): LazyRouteFunction<RouteObject> {
  return async () => {
    const result = await load();
    function Element() {
      const route = useCurrentRoute();
      const ac = useContext(ConfigContext);
      const lang = ac?.locale?.locale;
      useLayoutEffect(() => {
        if (route?.meta?.title && lang && route.meta.title[lang]) {
          document.title = route.meta.title[lang];
        }
      }, [lang, route?.meta?.title]);
      return createElement(result.default);
    }
    return { Component: Element };
  };
}

function hasPermissions(routes: RouterConfigObject[], authorities?: string[]) {
  return routes.filter((o) => {
    if (o?.meta?.permissions?.length) {
      if (!authorities) {
        return false;
      }
      const isSome = o.meta.permissions.some((x) => authorities.includes(x));

      if (!isSome) {
        return false;
      }
    }

    if (Array.isArray(o.children)) {
      o.children = hasPermissions(
        o.children as unknown as RouterConfigObject[],
        authorities,
      );
    }

    return true;
  });
}

export function permission(
  route: RouterConfigObject,
  authorities?: string[],
): RouterConfigObject[] {
  return hasPermissions([route], authorities);
}
